using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using LS.UnityExtensions;
using UnityEngine;

namespace LS.Digitize
{
    [UnityEngine.Scripting.Preserve]
    public abstract class CollectionSerializer : CompoundSerializer, ICollectionSerializer
    {
        public override bool IsCollection => true;

        public override bool IsUnsupported
        {
            get { return SubTypes == null || SubTypes.Count == 0; }
        }

        public ISerializer ElementSerializer => SubSerializer;

        public CollectionSerializer(Type type, Type elementType) : base(type, elementType)
        {

        }

        public virtual bool ReadICollection(IReader reader, ICollection<object> collection,
            ISerializer elementSerializer, out bool nullCollection)
        {
            bool result = true;
            nullCollection = false;
            using (var collectionScope = reader.ReadCollectionScope())
            {
                if (reader.CouldBeNull())
                {
                    reader.ReadNull();
                    nullCollection = true;
                    return false;
                }
                if (!collectionScope.ReadFailure)
                {
                    foreach (object value in new ReadCollectionItemEnumerable(reader, elementSerializer))
                    {
                        collection.Add(value);
                    }
                }
                else
                {
                    result = false;
                }
            }

            return result;
        }

        [UnityEngine.Scripting.Preserve]
        public virtual void ReadICollectionInto(IReader reader, ICollection collection,
            ISerializer elementSerializer)
        {
            throw new NotImplementedException();
        }

        public struct ReadCollectionItemEnumerable : IEnumerable
        {
            private IReader m_Reader;
            private ISerializer m_ElementSerializer;

            public ReadCollectionItemEnumerable(IReader reader, ISerializer elementSerializer)
            {
                this.m_Reader = reader;
                this.m_ElementSerializer = elementSerializer;
            }

            public IEnumerator GetEnumerator()
            {
                while (true)
                {
                    using (var itemScope = m_Reader.ReadCollectionItemScope())
                    {
                        if(itemScope.ReadFailure)
                            break;
                        yield return m_ElementSerializer.Read(m_Reader);
                    }
                }
            }
        }
    }

    [UnityEngine.Scripting.Preserve]
    public abstract class CollectionSerializer<T, TItem>: CompoundSerializer<T>, ICollectionSerializer<T, TItem> where T : ICollection<TItem>
    {

        public override bool IsCollection => true;

        public override bool IsUnsupported
        {
            get
            {
                return SubTypes == null || SubTypes.Count == 0;
            }
        }

        public ISerializer ElementSerializer => SubSerializer;

        public CollectionSerializer() : base(typeof(T), typeof(TItem))
        {
            
        }

        public override object Read(IReader reader)
        {
            return ReadData(reader);
        }
        
        public virtual void ReadInto(IReader reader, ref ICollection<TItem> obj)
        {
            throw new NotImplementedException();
        }

        public override void Write(IWriter writer, object obj)
        {
            WriteData(writer, (T)obj);
        }

        public override void ReadInto(IReader reader, ref T obj)
        {
            throw new NotImplementedException();
        }
        
        public virtual bool ReadICollection(IReader reader, ICollection<TItem> collection, ISerializer elementSerializer, out bool nullCollection)
        {
            bool result = true;
            nullCollection = false;
            using (var collectionScope = reader.ReadCollectionScope())
            {
                if (collectionScope.ReadFailure || reader.CouldBeNull())
                {
                    reader.ReadNull();
                    nullCollection = true;
                    return false;
                }
                
                if (!collectionScope.ReadFailure)
                {
                    foreach (TItem value in new ReadCollectionItemEnumerable<TItem>(reader,elementSerializer))
                    {
                        collection.Add(value);
                    }
                }
                else
                {
                    result = false;
                }
            }

            return result;
        }

        public virtual bool ReadICollection(IReader reader, ICollection<object> collection, ISerializer elementSerializer, out bool nullCollection)
        {
            bool result = true;
            nullCollection = false;
            using (var collectionScope = reader.ReadCollectionScope())
            {
                if (reader.CouldBeNull())
                {
                    reader.ReadNull();
                    nullCollection = true;
                    return false;
                }
                
                if (!collectionScope.ReadFailure)
                {
                    foreach (object value in new ReadCollectionItemEnumerable<object>(reader,elementSerializer))
                    {
                        collection.Add(value);
                    }
                }
                else
                {
                    result = false;
                }
            }

            return result;
        }

        public virtual void ReadICollectionInto(IReader reader, ICollection collection, ISerializer elementSerializer)
        {
            throw new NotImplementedException();
        }
        
        public struct ReadCollectionItemEnumerable<TElement> : IEnumerable<TElement>
        {
            private IReader m_Reader;
            private ISerializer m_ElementSerializer;

            public ReadCollectionItemEnumerable(IReader reader, ISerializer elementSerializer)
            {
                this.m_Reader = reader;
                this.m_ElementSerializer = elementSerializer;
            }

            IEnumerator<TElement> IEnumerable<TElement>.GetEnumerator()
            {
                ISerializer<TElement> genSerializer = m_ElementSerializer as ISerializer<TElement>;

                if (genSerializer != null)
                {
                    while (true)
                    {
                        using (var itemScope = m_Reader.ReadCollectionItemScope())
                        {
                            if(itemScope.ReadFailure)
                                break;
                            yield return genSerializer.ReadData(m_Reader);
                        }
                    }
                }
                else
                {
                    while (true)
                    {
                        using (var itemScope = m_Reader.ReadCollectionItemScope())
                        {
                            if(itemScope.ReadFailure)
                                break;
                            var item = m_ElementSerializer.Read(m_Reader);
                            if(item == null) 
                                yield return default(TElement);
                            else
                                yield return (TElement)item;
                        }
                    }
                }
            }

            public IEnumerator GetEnumerator()
            {
                while (true)
                {
                    using (var itemScope = m_Reader.ReadCollectionItemScope())
                    {
                        if(itemScope.ReadFailure)
                            break;
                        yield return m_ElementSerializer.Read(m_Reader);
                    }
                }
            }
        }
    }
}